﻿using System;
using System.Data;
using System.Diagnostics.Contracts;
using NHibernate;

namespace DataStore
{
    public class UnitOfWork : IDisposable
    {
        readonly ISession _session;
        internal ITransaction _transaction;

        /// <summary>
        /// Initializes a new instance of the <see cref="UnitOfWork"/> class.
        /// </summary>
        /// <param name="session">The session.</param>
        internal UnitOfWork(ISession session)
        {
            Contract.Requires<ArgumentNullException>(session != null);

            _session = session;
            _transaction = null;
        }

        /// <summary>
        /// Gets the session.
        /// </summary>
        /// <value>The session.</value>
        public ISession Session
        {
            get
            {
                return _session;
            }
        }

        /// <summary>
        /// Opens the DB connection.
        /// </summary>
        public void OpenDBConnection()
        {
            Contract.Requires<ApplicationException>(_session != null, "Session is null");

            if(!_session.IsConnected)
            {
                _session.Reconnect();
            }
        }

        /// <summary>
        /// Closes the DB connection.
        /// </summary>
        public void CloseDBConnection()
        {
            Contract.Requires<ApplicationException>(_session != null, "Session is null");

            if(_session.IsConnected)
            {
                _session.Disconnect();
            }
        }

        /// <summary>
        /// Begins a new transaction.
        /// </summary>
        /// <param name="isolationLevel">The isolation level.</param>
        public void BeginTransaction(IsolationLevel isolationLevel)
        {
            Contract.Requires<ApplicationException>(_transaction == null, "Transaction already exists. Nesting not supported.");

            _transaction = _session.BeginTransaction(isolationLevel);
        }

        /// <summary>
        /// Begins a new transaction.
        /// </summary>
        public void BeginTransaction()
        {
            BeginTransaction(IsolationLevel.ReadCommitted);
        }

        /// <summary>
        /// Commits the transaction.
        /// </summary>
        public void CommitTransaction()
        {
            Contract.Requires<ApplicationException>(_transaction != null, "Transaction is null");

            _transaction.Commit();
            _transaction = null;
        }

        /// <summary>
        /// Rollbacks the transaction.
        /// </summary>
        public void RollbackTransaction()
        {
            Contract.Requires<ApplicationException>(_transaction != null, "Transaction is null");

            _transaction.Rollback();
            _transaction = null;
        }

        /// <summary>
        /// Clears the session.
        /// </summary>
        public void Clear()
        {
            Contract.Requires<ApplicationException>(_session != null, "Session is null");

            _session.Clear();
        }

        /// <summary>
        /// Closes the session.
        /// </summary>
        public void Close()
        {
            Contract.Requires<ApplicationException>(_session != null, "Session is null");

            _session.Close();
        }

        /// <summary>
        /// Locks the specified object.
        /// </summary>
        /// <param name="o">The object being locked.</param>
        /// <param name="lockMode">The lock mode.</param>
        public void Lock(object o, LockMode lockMode)
        {
            Contract.Requires<ApplicationException>(_session != null, "Session is null");

            _session.Lock(o, lockMode);
        }

        /// <summary>
        /// Flushes the session.
        /// </summary>
        public void Flush()
        {
            _session.Flush();
        }

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="UnitOfWork"/> is reclaimed by garbage collection.
        /// </summary>
        ~UnitOfWork()
        {
            Dispose(false);
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        internal virtual void Dispose(bool disposing)
        {
            if(disposing)
            {
                if(_transaction != null)
                {
                    RollbackTransaction();
                }
                if(_session != null)
                {
                    _session.Close();
                }
            }
        }

        #region Implementation of IDisposable

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <filterpriority>2</filterpriority>
        public  void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}
